/*
  Copyright 2003-2005,2009 Ronald S. Burkey <info@sandroid.org>
  
  This file is part of yaAGC.

  yaAGC is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  yaAGC is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with yaAGC; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  
  Filename:	main.c
  Contact:	Ron Burkey <info@sandroid.org>
  Reference:	http://www.ibiblio.org/apollo/index.html
  Mods:		08/20/03 RSB.	First fully-working version.
  		07/11/04 RSB.	Flash-rate change from 1 cycle per second
				to 1.5 cps. per AC's "APOLLO GUIDANCE AND 
				NAVIGATION SYSTEM LUNAR MODULE STUDENT STUDY 
				GUIDE".  Also, instead of flashing the NOUN 
				or VERB labels, we now flash the NOUN or VERB 
				digits.  Furthermore, OPR ERR and KEY REL flash.
		07/19/04 RSB.	--help messages adjusted for crippled Win32
				command-line length.
		08/11/04 RSB	Added the --half-size option.
		08/21/04 RSB	Added StartupDelay et al.
		05/14/05 RSB	Added and corrected website references.
		05/15/05 RSB	Added CounterDebugMode, for debugging
				yaAGC counter increments.
		05/29/05 RSB	In the Pulse() function, rewrote the
				conditions for buffering incoming 
				socket data to take into account the new
				idea of using packet signatures rather
				than the strict sequence 00 01 10 11.
		06/26/05 RSB	Added the --test-uplink switch.
		06/28/05 RSB	Added --test-downlink, to separate that
				functionality from --test-uplink.
		03/05/09 RSB	Added --relative-pixmaps to allow having
				a relative rather than an absolute path
				to pixmaps, for the installation-directory
				setup contemplated with the new VirtualAGC
				program.
		03/07/09 RSB	Replaced all .xpm graphics by .jpg graphics
				in an attempt to see if I can fix some 
				portability problems by doing so.  Auto-
				convert .xpm settings found in --cfg files.
				Also, correct some default setups which 
				would cause a segfault if --cfg wasn't used.
		03/08/09 RSB	Fixed that fact that Pulse() was continually
				updating KeyRel and OprErr at the flash rate
				even if the graphic being updated hadn't
				actually changed.
*/

/*
 * Initial main.c file generated by Glade. Edit as required.
 * Glade will not overwrite this file.
 */

//#define VER(x) #x

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <gtk/gtk.h>

#include "interface.h"
#include "support.h"
#include "callbacks.h"

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

#include "yaAGC.h"
#include "agc_engine.h"

int HalfSize = 0;
#define PULSE_INTERVAL 100
gint Pulse (gpointer data);
char *Hostname = "localhost";
//int Portnum = 19697;
GtkWidget *window1 = NULL;
int ParseCfg (GtkWidget *, char *Filename);
#ifdef WIN32
static int StartupDelay = 500;
#else
static int StartupDelay = 0;
#endif
int DebugCounterMode = 0;
// TestUplink is set when we want to test the digital uplink by emitting
// keycodes on the digital uplink rather than on the DSKY channels.
int TestUplink = 0, TestDownlink = 0;
int RelativePixmaps = 0;

//------------------------------------------------------------------------

#define DEBUG(x) //printf ("I am here: %d\n", x)

int
main (int argc, char *argv[])
{
  GtkWidget *hCreateMainWindow (void);
  int i, j;
  int UsedCfg = 0;
  
#ifdef ENABLE_NLS
  bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
  textdomain (GETTEXT_PACKAGE);
#endif

  gtk_set_locale ();
  gtk_init (&argc, &argv);

  printf ("yaDSKY Apollo DSKY simulation, ver " NVER ", built " __DATE__ " " __TIME__ "\n");
  printf ("Copyright 2003-2005,2009 by Ronald S. Burkey\n");
  printf ("Refer to http://www.ibiblio.org/apollo/index.html for more information.\n");
	  
  //add_pixmap_directory (PACKAGE_DATA_DIR "/" PACKAGE "/pixmaps");
  printf ("Relative pixmaps\n");
  RelativePixmaps = 1;
  add_pixmap_directory ("pixmaps/yaDSKY");
  DEBUG (-1);

  for (i = 1; i < argc; i++)
    {
      printf ("Arg %d = \"%s\"\n", i, argv[i]);
      if (!strcmp (argv[i], "--relative-pixmaps"))
        {
	  //printf ("Relative pixmaps\n");
          //RelativePixmaps = 1;
	  //add_pixmap_directory ("pixmaps/yaDSKY");
	}
      else if (!strcmp (argv[i], "--test-uplink"))
        TestUplink = 1;
      else if (!strcmp (argv[i], "--test-downlink"))
        TestDownlink = 1;
      else if (!strncmp (argv[i], "--ip=", 5))
        Hostname = &argv[i][5];
      else if (!strncmp (argv[i], "--port=", 7))
        {
          Portnum = atoi (&argv[i][7]);
	  if (Portnum <= 0 || Portnum >= 0x10000)
	    {
	      printf ("The --port switch is out of range.  Must be 1-64K.\n");
	      return (1);
	    }
	}
      else if (!strncmp (argv[i], "--cfg=", 6))
        {
	  UsedCfg = 1;
	  if (window1 == NULL)
	    window1 = create_MainWindow ();
	  if (ParseCfg (window1, &argv[i][6]))
	    {
	      printf ("Aborting due to --cfg errors.\n");
	      return (1);
	    }
	}
      else if (!strcmp (argv[i], "--half-size"))
        {
	  if (window1 == NULL)
	    window1 = hCreateMainWindow ();
	  else
	    {
	      printf ("The --half-size switch must precede the --cfg switch.\n");
	      return (1);
	    }
          HalfSize = 1;
	}
      else if (1 == sscanf (argv[i], "--delay=%d", &j))
        StartupDelay = j;
      else if (!strcmp (argv[i], "--debug-counter-mode"))
        DebugCounterMode = 1;
      else
        {
	  printf ("USAGE:\n");
	  printf ("\tyadsky [OPTIONS]\n");
	  printf ("The available options are:\n");
	  printf ("--ip=Hostname\n");
	  printf ("\tThe yaDSKY program and the yaAGC Apollo Guidance Computer simulation\n");
	  printf ("\texist in a \"client/server\" relationship, in which the yaDSKY program\n");
	  printf ("\tneeds to be aware of the IP address or symbolic name of the host \n");
	  printf ("\tcomputer running the yaAGC program.  By default, this is \"localhost\",\n");
	  printf ("\tmeaning that both yaDSKY and yaAGC are running on the same computer.\n");
	  printf ("--port=Portnumber\n");
	  printf ("\tBy default, yaDSKY attempts to connect to the yaAGC program using port\n");
	  printf ("\tnumber %d.  However, if more than one instance of yaDSKY is being\n",
	          Portnum);
	  printf ("\trun, or if yaAGC has been configured to listen on different ports, then\n");
	  printf ("\tdifferent port settings for yaDSKY are needed.  Note that by default,\n");
	  printf ("\tyaAGC listens for new connections on ports %d-%d.\n",
	          Portnum, Portnum + 10 - 1);
	  printf ("--cfg=ConfigFilename\n");
	  printf ("\tSelects a configuration file to be used, to allow different yaDSKY\n");
	  printf ("\tsettings for LM vs. CM, or for different Apollo missions.  The \n");
	  printf ("\tconfiguration files presently known are LM.ini, CM.ini, and CM0.ini. \n");
	  printf ("\tBy default (no --cfg switch) LM settings are used, but not the LM.ini\n");
	  printf ("\tfile itself.\n");
	  printf ("--half-size\n");
	  printf ("\tUses a half-size version of yaDSKY, suitable for smaller graphical\n");
	  printf ("\tdisplays.  If present, must precede --cfg.\n");
	  printf ("--delay=N\n");
	  printf ("\t\"Start-up delay\", in ms.  Defaults to %d.  What the start-up\n", StartupDelay);
	  printf ("\tdelay does is to prevent yaDSKY from attempting to communicate with\n");
	  printf ("\tyaAGC for a brief time after power-up.  This option is really only\n");
	  printf ("\tuseful in Win32, to work around a problem with race-conditions in\n");
	  printf ("\tthe start-up scripts like SimLuminary131.  When the race problem is\n");
	  printf ("\tfixed correctly, this option will probably no longer be useful.\n");
	  printf ("--debug-counter-mode\n");
	  printf ("\tThis is present only for debugging yaAGC\'s \"unprogrammed\"\n");
	  printf ("\tcounter-increments, and has no useful purpose otherwise.  In this\n");
	  printf ("\tthe simulated DSKY\'s do not have their normal interpretations and\n");
	  printf ("\tcannot be used to affect the AGC CPU in any normal way.  Instead,\n");
	  printf ("\tthe combination NOUN-digit-digit is used to specify an (octal)\n");
	  printf ("\tCPU register number, and the combination VERB-digit is used\n");
	  printf ("\tto select a counter-increment type as defined on the Virtual AGC\n");
	  printf ("\twebsite\'s developer page.  The PRO key is used to send the selected\n");
	  printf ("\tcounter-increment command to the AGC.  Note that there are no\n");
	  printf ("\tvisual displays associated with this, since the AGC is still\n");
	  printf ("\tcommanding the DSKY visual display in its usual way.  This mode\n");
	  printf ("\tis terminated only by restarting yaDSKY.  Only counter registers\n");
	  printf ("\tin the range 32-60 (octal, default 32) are accepted, and only \n");
	  printf ("\tincrement-types in the range 0-6 (default 1) are accepted.\n");
	  printf ("--test-uplink\n");
	  printf ("\tIf this switch is used, keypresses are communicated to yaAGC\n");
	  printf ("\tas digital-uplink date instead of regular DSKY data.\n");
	  printf ("--test-downlink\n");
	  printf ("\tIn this mode, messages will also be printed whenever a downlink list is\n");
	  printf ("\tdetected. (Highly recommended.  Probably should be the default.)\n");
	  printf ("--relative-pixmaps\n");
	  printf ("\tUses ./pixmaps/yaDSKY/ as the directory in which to look for \n");
	  printf ("\tpixmaps rather than the normal installation directory for them.\n");
	  printf ("\tThis relates to the directory setup used when running yadsky under\n");
	  printf ("\tthe VirtualAGC GUI front-end.  Must precede --cfg and --half-size.\n");
	  return (0);
	}	
    }
  DEBUG (0);
  printf ("Hostname=%s, port=%d\n", Hostname, Portnum);	
  	
  /*
   * The following code was added by Glade to create one of each component
   * (except popup menus), just so that you see something after building
   * the project. Delete any components that you don't want shown initially.
   */
  DEBUG (1);
  if (window1 == NULL)
    window1 = create_MainWindow ();
    
  // The following is sort of an abbreviated form of some stuff that 
  // ParseCfg does, and is called in case --cfg didn't appear on the 
  // command-line.
  if (!UsedCfg)
    {
      extern void my_gtk_image_set_from_file (GtkImage *Image, const char *Filename);
      extern Ind_t Inds[14];
      Ind_t *Indptr;
      extern GtkImage *KeyRelAnnunciator, *OprErrAnnunciator, *iLastButton;
      extern const char *CurrentKeyRel, *CurrentOprErr;
      extern const char *BlankKeyRel, *BlankOprErr;
      // Update all of the indicator legends.  
      for (Indptr = Inds, i = 0; i < 14; Indptr++, i++)
	my_gtk_image_set_from_file (Indptr->Widget, Indptr->GraphicOff);
      my_gtk_image_set_from_file (iLastButton, "ProUp.jpg");
      KeyRelAnnunciator = Inds[3].Widget;
      CurrentKeyRel = BlankKeyRel = Inds[3].GraphicOff;
      my_gtk_image_set_from_file (KeyRelAnnunciator, CurrentKeyRel);
      OprErrAnnunciator = Inds[4].Widget;
      CurrentOprErr = BlankOprErr = Inds[4].GraphicOff;
      my_gtk_image_set_from_file (OprErrAnnunciator, CurrentOprErr);
    }
    
  DEBUG (2);
  gtk_widget_show (window1);
  DEBUG (3);

  // Sets up the Pulse function to be called every PULSE_INTERVAL ms.
  g_timeout_add (PULSE_INTERVAL, Pulse, NULL);
  DEBUG (4);

  gtk_main ();
  return 0;
}

//-------------------------------------------------------------------------
// Manage server connection.

int ServerSocket = -1;

gint
Pulse (gpointer data)
{
#ifdef gtk_image_set_from_file
#undef gtk_image_set_from_file
#endif
#define gtk_image_set_from_file my_gtk_image_set_from_file
  extern void my_gtk_image_set_from_file (GtkImage *Image, const char *Filename);
  extern int VerbNounFlashing;
  static unsigned char Packet[4];
  static int PacketSize = 0;
  static int FlashCounter = 1, FlashStatus = 0;
  int i;
  unsigned char c;
  static const char *LastKeyRel = NULL, *LastOprErr = NULL;
  
  if (StartupDelay > 0)
    {
      StartupDelay -= PULSE_INTERVAL;
      return (TRUE);
    }
  // If the noun/verb-flash flag is set, then flash them.
  if (!--FlashCounter)
    {
      extern GtkImage *VD1Digit, *VD2Digit, *ND1Digit, *ND2Digit,
		      *OprErrAnnunciator, *KeyRelAnnunciator;
      extern const char *CurrentVD1, *CurrentVD2, *CurrentND1, 
			*CurrentND2, *CurrentBlank,
			*CurrentOprErr, *CurrentKeyRel,
			*BlankOprErr, *BlankKeyRel;
      if (CurrentVD1 == CurrentBlank && CurrentVD2 == CurrentBlank &&
          CurrentND1 == CurrentBlank && CurrentND2 == CurrentBlank &&
	  CurrentKeyRel == BlankKeyRel && CurrentOprErr == BlankOprErr)
        FlashCounter = 1;
      else
        FlashCounter = 10;
      if (FlashStatus)
	{
	  if (VerbNounFlashing)
	    {
	      gtk_image_set_from_file (VD1Digit, CurrentVD1);
	      gtk_image_set_from_file (VD2Digit, CurrentVD2);
	      gtk_image_set_from_file (ND1Digit, CurrentND1);
	      gtk_image_set_from_file (ND2Digit, CurrentND2);
	    }
	  if (LastKeyRel != CurrentKeyRel)
	    {
	      gtk_image_set_from_file (KeyRelAnnunciator, CurrentKeyRel);
	      LastKeyRel = CurrentKeyRel;
	    }
	  if (LastOprErr != CurrentOprErr)
	    {
	      gtk_image_set_from_file (OprErrAnnunciator, CurrentOprErr);
	      LastOprErr = CurrentOprErr;
	    }
	}
      else
	{
	  if (VerbNounFlashing)
	    {
	      gtk_image_set_from_file (VD1Digit, CurrentBlank);
	      gtk_image_set_from_file (VD2Digit, CurrentBlank);
	      gtk_image_set_from_file (ND1Digit, CurrentBlank);
	      gtk_image_set_from_file (ND2Digit, CurrentBlank);
	    }
	  if (LastKeyRel != BlankKeyRel)
	    {
	      gtk_image_set_from_file (KeyRelAnnunciator, BlankKeyRel);
	      LastKeyRel = BlankKeyRel;
	    }
	  if (LastOprErr != BlankOprErr)
	    {
	      gtk_image_set_from_file (OprErrAnnunciator, BlankOprErr);
	      LastOprErr = BlankOprErr;
	    }
	}
      FlashStatus = !FlashStatus;
    }
  // Try to connect to the server (yaAGC) if not already connected.
  if (ServerSocket == -1)
    {
      ServerSocket = CallSocket (Hostname, Portnum);
      if (ServerSocket != -1)
        printf ("yaDSKY is connected.\n");
    }
  if (ServerSocket != -1)
    {
      for (;;)
        {
	  i = recv (ServerSocket, &c, 1, MSG_NOSIGNAL);
	  if (i == -1)
	    {
	      // The condition i==-1,errno==0 occurs only on Win32, and 
	      // I'm not sure exactly what it corresponds to---I assume 
	      // means "no data" rather than error.
	      if (errno == EAGAIN || errno == 0)
	        i = 0;
	      else
	        {	
		  printf ("yaDSKY reports server error %d\n", errno);
		  close (ServerSocket);
		  ServerSocket = -1;
		  break;
	        }
	    }
	  if (i == 0)
	    break;
	  // This (newer) code will accept any packet signature of the form
	  // 00 XX XX XX.
	  if (0 == (0xc0 & c))
	    PacketSize = 0;
	  if (PacketSize != 0 || (0xc0 & c) == 0)	      
	    { 
	      Packet[PacketSize++] = c;
	      if (PacketSize >= 4)
		{
		  void ActOnIncomingIO (GtkWidget *widget, unsigned char *Packet);
		  ActOnIncomingIO (window1, Packet);
		  PacketSize = 0;   
		}  
	    }
	}
    }
  return (TRUE);    
}

